/* ------------------------------------------------------------------------*
 * 数据模式分为字节模式和字模式
 * 字模式 数据区基址应对齐在4字节边界，数据长度为4的倍数
 * MSB模式不可混用
 * LSB模式可混用
 * 为使用字模式加速读写，数据收发采用LSB模式
 * ------------------------------------------------------------------------*
 * 硬件FIFO的使用：
 * 硬件FIFO可加速读写速度，但要注意外部中断可能导致的RX溢出问题
 * 可以采用如下两种方式解决：
 * a.开启DMA接收数据
 * b.保持发送FIFO始终不满,并且每发送一个数据的到TX-FIFO后，检测RX将其读空。
 *   实测32MHz外频时不会造成速度损失
 * ------------------------------------------------------------------------*/
#include "bsp.h" 
#include "bsp_spi_nor.h"
#include "nor_cmd.h"
#include "pincfg.h"

#define NOR_SPI		QSPI0
#define ISR_IRQn	QSPI0_IRQn

uint8_t b_io_q_mode = 0;

/*****************************************************************************
* @brief   PIN MOD
* 注意如果系统中有多个位置需要操作该寄存器，须关中断
*****************************************************************************/
__STATIC_INLINE void D2D3_SwitchToNormalMode(void)
{
	uint32_t mode;
	
	// MFP SET
	SYS->GPA_MFPL = SYS->GPA_MFPL & ~(SYS_GPA_MFPL_PA4MFP_Msk | SYS_GPA_MFPL_PA5MFP_Msk);
	
	// PIN MODE SET
	mode = PA->MODE;
	mode &= ~(0x3ul << (4 << 1));
	mode &= ~(0x3ul << (5 << 1));
	mode |= GPIO_MODE_OUTPUT << (4 << 1);
	mode |= GPIO_MODE_OUTPUT << (5 << 1);
	PA->MODE = mode;
	
	// HOLD AND WP
	PA4 = 1;
	PA5 = 1;
}

__STATIC_INLINE void D2D3_SwitchToQuadMode(void)
{
	uint32_t mfp;
	
	mfp = SYS->GPA_MFPL;
	mfp &= ~(SYS_GPA_MFPL_PA4MFP_Msk | SYS_GPA_MFPL_PA5MFP_Msk);
	mfp |= SYS_GPA_MFPL_PA4MFP_QSPI0_MOSI1 | SYS_GPA_MFPL_PA5MFP_QSPI0_MISO1;
	SYS->GPA_MFPL = mfp;
}

/*****************************************************************************
* @brief   hal init.
* @param   none
* @return  none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void nor_spi_init()
{
	/* Configure QSPI_FLASH_PORT as a master, MSB first, 8-bit transaction, QSPI Mode-0 timing, clock is 20MHz */
	QSPI_Open(NOR_SPI, QSPI_MASTER, QSPI_MODE_0, 8, 32000000);

	// 发送FIFO阈值
	NOR_SPI->FIFOCTL = (NOR_SPI->FIFOCTL & ~QSPI_FIFOCTL_TXTH_Msk) | (4 << QSPI_FIFOCTL_TXTH_Pos);
	
	// 帧间隔
	NOR_SPI->CTL = (NOR_SPI->CTL & ~QSPI_CTL_SUSPITV_Msk) | (1UL << QSPI_CTL_SUSPITV_Pos);
	
	D2D3_SwitchToNormalMode();

	b_io_q_mode = 0;
}



/*****************************************************************************
* @brief   cs ctr.
*
* @param   none
*
* @return  none
*
* @ Pass/ Fail criteria: none
*****************************************************************************/
void nor_spi_cs(uint8_t st)
{
	if (st)
	{
		nor_ss.en();
		NOP20();
	}
	else
	{
		nor_ss.dis();
		NOP5();
	}
}

void w25n_cs(uint8_t st)
{
	if (st)
	{
		nand_ss.en();
		NOP20();
	}
	else
	{
		nand_ss.dis();
		NOP5();
	}
}


/*****************************************************************************
* @brief   spi mode select.
*****************************************************************************/
void nor_spi_mode(nor_spi_type mode)
{
	switch (mode)
	{
		case SPI_NOR_QPI_READ_MODE:
			D2D3_SwitchToQuadMode();
			QSPI_ENABLE_QUAD_INPUT_MODE(NOR_SPI);
			b_io_q_mode = 1;
			break;
		case SPI_NOR_QPI_WRITE_MODE:
			D2D3_SwitchToQuadMode();
			QSPI_ENABLE_QUAD_OUTPUT_MODE(NOR_SPI);
			b_io_q_mode = 1;
			break;
		default:
			QSPI_DISABLE_QUAD_MODE(NOR_SPI);
			if (b_io_q_mode)
				D2D3_SwitchToNormalMode();
			b_io_q_mode = 0;
			break;
	}
}

/*****************************************************************************
* @brief   spi speed.
*****************************************************************************/
void nor_spi_high_speed(void)
{

}

/*****************************************************************************
* @brief   spi speed.
*****************************************************************************/
void nor_spi_low_speed(void)
{

}

/*****************************************************************************
* @brief   spi delay.
*****************************************************************************/
#define nor_spi_read_delay()		NOP1()
#define gd25_wait_rst_comp()		while(NOR_SPI->STATUS & QSPI_STATUS_TXRXRST_Msk)
#define gd25_rx_reset()				NOR_SPI->FIFOCTL |= QSPI_FIFOCTL_RXRST_Msk
#define QSPI_FIFO_TXFULL_FLAG(x)	(0 == (x->STATUS & QSPI_STATUS_TXTHIF_Msk))

/*****************************************************************************
* @brief   spi data read.
*****************************************************************************/
uint8_t nor_spi_rw(uint8_t dataW)
{
	gd25_wait_rst_comp();

	//send data
	QSPI_WRITE_TX(NOR_SPI, dataW);
	nor_spi_read_delay();
	
	//wait for trans complete
	while (QSPI_IS_BUSY(NOR_SPI));

	//read data
	return (QSPI_READ_RX(NOR_SPI));
}

/*****************************************************************************
* @brief   spi data write.
*****************************************************************************/
uint8_t nor_spi_cmd_send(const uint8_t *dataw, uint8_t Len)
{
	gd25_wait_rst_comp();
	
	while (Len--)
	{
		//send data
		QSPI_WRITE_TX(NOR_SPI, *dataw++);
		nor_spi_read_delay();
		
		//wait for tx fifo
		while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI));
	}

	//wait for trans complete
	while (QSPI_IS_BUSY(NOR_SPI));

	// clear RX buffer
	gd25_rx_reset();

	return (0);
}

uint8_t nor_qpi_cmd_send(const uint8_t *dataw, uint8_t Len)
{
	return nor_spi_cmd_send(dataw, Len);
}

/*****************************************************************************
* @brief   spi data write.
*****************************************************************************/
uint8_t nor_spi_write(const uint8_t *dataw, uint32_t Len)
{
	gd25_wait_rst_comp();
	
	QSPI_SET_LSB_FIRST(NOR_SPI);
	
	// 8 bits mode to alain 4B
	if((uint32_t)dataw & 0x03)
	{	
		uint32_t len_alain = 4 - ((uint32_t)dataw & 0x03);
		Len -= len_alain;
		
		while (len_alain--)
		{
			//send data
			QSPI_WRITE_TX(NOR_SPI, *dataw++);
			nor_spi_read_delay();

			//wait for trans complete
			while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI));
		}

		//wait for trans complete
		while (QSPI_IS_BUSY(NOR_SPI));
	}
	
	// 32 bits mode
	if(Len >= 4)
	{
		uint32_t *prd32, len32;

		// 32bits mode
		QSPI_SET_DATA_WIDTH(NOR_SPI, 32);

		// data info
		len32 = Len >> 2;
		prd32 = (uint32_t *)dataw;
		
		// send
		while (len32--)
		{
			//send data
			QSPI_WRITE_TX(NOR_SPI, *prd32++);
			nor_spi_read_delay();

			//wait for trans complete
			while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI));
		}

		//wait for trans complete
		while (QSPI_IS_BUSY(NOR_SPI));

		QSPI_SET_DATA_WIDTH(NOR_SPI, 8);
		
		// remain
		dataw = (uint8_t *) prd32;
		Len &= 3;
	}
	
	// 8 bits mode
	if (Len)
	{
		while (Len--)
		{
			//send data
			QSPI_WRITE_TX(NOR_SPI, *dataw++);
			nor_spi_read_delay();

			//wait for trans complete
			while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI));
		}

		//wait for trans complete
		while (QSPI_IS_BUSY(NOR_SPI));
	}
	
	// clear RX buffer
	gd25_rx_reset();
	
	QSPI_SET_MSB_FIRST(NOR_SPI);
	
	return (0);
}


/*****************************************************************************
* @brief   spi data read.
*****************************************************************************/
#define GD25_READ_U8()	while((NOR_SPI->STATUS & QSPI_STATUS_RXEMPTY_Msk) == 0) *datar++ = QSPI_READ_RX(NOR_SPI)
#define GD25_READ_U32()	while((NOR_SPI->STATUS & QSPI_STATUS_RXEMPTY_Msk) == 0) *prd32++ = QSPI_READ_RX(NOR_SPI)
	
uint8_t nor_spi_read(uint8_t *datar, uint32_t Len)
{
	gd25_wait_rst_comp();

	QSPI_SET_LSB_FIRST(NOR_SPI);

	// 8 bits mode to alain 4B
	if((uint32_t)datar & 0x03)
	{
		uint32_t len_alain = 4 - ((uint32_t)datar & 0x03);
		Len -= len_alain;
		
		while (len_alain--)
		{
			//send data
			QSPI_WRITE_TX(NOR_SPI, 0xFF);
			nor_spi_read_delay();
			
			// rx not empty ## read data
			GD25_READ_U8();

			//wait for tx fifo
			while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI)) GD25_READ_U8();
		}
		
		//wait for trans complete
		while (QSPI_IS_BUSY(NOR_SPI)) GD25_READ_U8();
		
		// rx not empty ## read data
		GD25_READ_U8();
	}

	// 32 bits mode
	if(Len >= 4)
	{
		uint32_t *prd32, len32;
		
		// 32bits mode
		QSPI_SET_DATA_WIDTH(NOR_SPI, 32);

		// data info
		len32 = Len >> 2;
		prd32 = (uint32_t *)datar;
		
		while (len32--)
		{
			//send data
			QSPI_WRITE_TX(NOR_SPI, 0xFFFFFFFF);
			nor_spi_read_delay();

			// rx not empty ## read data
			GD25_READ_U32();

			//wait for tx fifo
			while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI)) GD25_READ_U32();
		}
		
		//wait for trans complete
		while (QSPI_IS_BUSY(NOR_SPI)) GD25_READ_U32();
		
		// rx not empty ## read data
		GD25_READ_U32();

		QSPI_SET_DATA_WIDTH(NOR_SPI, 8);
		
		// remain
		datar = (uint8_t *) prd32;
		Len &= 3;
	}

	// 8 bits mode
	if (Len)
	{
		while (Len--)
		{
			//send data
			QSPI_WRITE_TX(NOR_SPI, 0xFF);
			nor_spi_read_delay();
			
			// rx not empty ## read data
			GD25_READ_U8();

			//wait for tx fifo
			while (QSPI_FIFO_TXFULL_FLAG(NOR_SPI)) GD25_READ_U8();
		}
		
		//wait for trans complete
		while (QSPI_IS_BUSY(NOR_SPI)) GD25_READ_U8();
		
		// rx not empty ## read data
		GD25_READ_U8();
	}
	
	QSPI_SET_MSB_FIRST(NOR_SPI);

	return 0;
}

/*****************************************************************************
* @brief   qpi data write.
*****************************************************************************/
uint8_t nor_qpi_write(const uint8_t *dataw, uint32_t Len)
{
	nor_spi_write(dataw, Len);
	return (0);
}

/*****************************************************************************
* @brief   qpi data read.
*****************************************************************************/
uint8_t nor_qpi_read(uint8_t *datar, uint32_t Len)
{
	nor_spi_read(datar, Len);

	return (0);
}
